﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FunctionalLayer;

namespace ControlLayer
{
    public class CarController
    {

        #region Fields
        private Arduino _arduino;
        private Pit _pit;
        private DataStorage _dataStorage;
        private DataStorage _dataStorageError;
        private Lap _lap;
        private SpeedAdvisor _speedAdvisor;

        private TimeSpan _maximumTime;

        private int _CurrentSectionNumber;
        /// <summary>
        /// The current section the car is in
        /// </summary>
        public int CurrentSectionNumber
        {
            get { return _CurrentSectionNumber; }
            set { _CurrentSectionNumber = value; }
        }
        private double _amperage;
        /// <summary>
        /// The amperage the engine uses
        /// </summary>
        public double Amperage
        {
            get { return _amperage; }
            set { _amperage = value; }
        }
        private double _distance;
        /// <summary>
        /// The total distance the car drived from initialization
        /// </summary>
        public double Distance
        {
            get { return _distance; }
            set { _distance = value; }
        }
        private double _speed;
        /// <summary>
        /// The current speed of the car
        /// </summary>
        public double Speed
        {
            get { return _speed; }
            set { _speed = value; }
        }
        private double _advisedSpeed;
        /// <summary>
        /// The minimum speed that is needed to finish in time
        /// </summary>
        public double AdvisedSpeed
        {
            get { return _advisedSpeed; }
            set { _advisedSpeed = value; }
        }
        private double _voltage;
        /// <summary>
        /// The voltage the engine uses
        /// </summary>
        public double Voltage
        {
            get { return _voltage; }
            set { _voltage = value; }
        }
        private double _wattage;
        /// <summary>
        /// The wattage the car uses
        /// </summary>
        public double Wattage
        {
            get { return _wattage; }
            set { _wattage = value; }
        }
        private double _temperature;
        /// <summary>
        /// The temperature of the engine
        /// </summary>
        public double Temperature
        {
            get { return _temperature; }
            set { _temperature = value; }
        }
        private bool _engineOverheat;
        /// <summary>
        /// True if the egnine overheats. 
        /// If this happens the Arduino will shut down the power, so the car will stop.
        /// </summary>
        public bool BatteryOverheat
        {
            get { return _engineOverheat; }
            set { _engineOverheat = value; }
        }

        //Variable which is used to determine if we would like to log or not
        private bool _doLog;

        //This is for noticing the main form that there is new data to show
        public event Action EventNewData;

        #endregion


        public CarController()
        {
            Init();
        }

        /// <summary>
        /// When the car object is destroyed this method is called. 
        /// The logger, arduino communication and pit communication is stopped.
        /// </summary>
        ~CarController()
        {
            CloseCOMPorts();
            StopLogging();
            StopLoggingError();
        }

        /// <summary>
        /// Inits all the needed objects
        /// </summary>
        private void Init()
        {
            _arduino = Arduino.GetInstance;
            _dataStorage = new DataStorage(ConfigSingleton.Settings.LogFilePath);
            _dataStorageError = new DataStorage(ConfigSingleton.Settings.LogFilePathError);
            _dataStorageError.StartLoggingError();
            _lap = new Lap();
            _lap.Start();
            _pit = new Pit();
            _speedAdvisor = new SpeedAdvisor();
            _maximumTime = ConfigSingleton.Settings.MaxRaceTime;
            _doLog = false;
            _arduino.EventNewData += _arduino_EventNewData;
            _arduino.OnNewSection += _arduino_OnNewSection;

            _arduino.ExceptionOccured += _exceptionOccured;
            _dataStorage.ExceptionOccured += _exceptionOccured;
            _dataStorageError.ExceptionOccured += _exceptionOccured;
            _pit.ExceptionOccured += _exceptionOccured;

            _arduino.Init();
        }

        /// <summary>
        /// This method is called when an exeption occurs.
        /// This method will log the exeption.
        /// </summary>
        /// <param name="message"></param>
        void _exceptionOccured(string message)
        {
            _dataStorageError.Log(message);
        }

        /// <summary>
        /// [EVENT] Gets raised when car enters new section
        /// </summary>
        /// <param name="sender">Arduino.cs</param>
        void _arduino_OnNewSection()
        {
            _lap.NewSection();
        }

        /// <summary>
        /// This method gets used by the form to get the time data.
        /// </summary>
        /// <returns>
        /// 0: Currect time
        /// 1: Previous time
        /// 2: Advice time
        /// 3: Total time
        /// 4: Time left
        /// </returns>
        public List<TimeSpan> GetTimeData()
        {
            List<TimeSpan> list = new List<TimeSpan>();

            int section = _lap.CurrentSectionNumber;
            TimeSpan currentTime = _lap.GetCurrentTime();
            //TimeSpan previousTime = _lap.GetPreviousSectionTime(section);
            TimeSpan previousTime = _lap.GetPreviousLapTime();
            TimeSpan totalTime = _lap.GetTotalTime();
            TimeSpan timeLeft = _lap.GetTimeLeft(totalTime, _maximumTime);
            TimeSpan adviceTime = _lap.GetAdviceTime(timeLeft);

            list.Add(currentTime);
            list.Add(previousTime);
            list.Add(adviceTime);
            list.Add(totalTime);
            list.Add(timeLeft);


            return list;
        }

        /// <summary>
        /// This method is used to manualy add a lap.
        /// </summary>
        /// <returns></returns>
        public int NewLap()
        {
            return _lap.NewLap();
        }

        /// <summary>
        /// [EVENT] Gets raised when there is new data from the arduino board
        /// </summary>
        /// <param name="amperage">the amperage</param>
        /// <param name="distance">the total distance</param>
        /// <param name="speed">the speed</param>
        /// <param name="voltage">the voltage</param>
        /// <param name="wattage">the wattage</param>
        void _arduino_EventNewData()
        {
            _amperage = _arduino.Amperage;
            _distance = _arduino.Distance;
            _speed = _arduino.Speed;
            _voltage = _arduino.Voltage;
            _wattage = _arduino.Wattage;
            _temperature = _arduino.Temperature;
            _engineOverheat = _arduino.Overheat;
            _CurrentSectionNumber = _lap.CurrentSectionNumber;

            TimeSpan totalTime = _lap.GetTotalTime();
            TimeSpan timeLeft = _lap.GetTimeLeft(totalTime, _maximumTime);
            _advisedSpeed = _speedAdvisor.Calculate(_distance, timeLeft);

            if (_doLog)
            {
                _dataStorage.Log(new string[] { _distance.ToString(), _speed.ToString(), _lap.CurrentSectionNumber.ToString(), _amperage.ToString(), _voltage.ToString(), _wattage.ToString(), _temperature.ToString() });
            }

            BufferData buffer = new BufferData();
            buffer.timeDate = DateTime.Now;
            buffer.amperage = _amperage;
            buffer.distance = _distance;
            buffer.section = _CurrentSectionNumber;
            buffer.speed = _speed;
            buffer.voltage = _voltage;
            buffer.wattage = _wattage;
            buffer.temperature = _temperature;
            _pit.SendToPit(buffer);

            //Raise new data event. This is for noticing the main form that there is new data to show
            EventNewData();
        }

        /// <summary>
        /// Starts logging to file
        /// </summary>
        public int StartLogging()
        {
            _doLog = true;
            return _dataStorage.StartLogging();
        }

        /// <summary>
        /// Stops logging to file
        /// </summary>
        public int StopLogging()
        {
            _doLog = false;
            return _dataStorage.StopLogging();
        }

        /// <summary>
        /// Stops the error logging
        /// </summary>
        public int StopLoggingError()
        {
            _doLog = false;
            return _dataStorageError.StopLogging();
        }

        /// <summary>
        /// Closes the COM ports
        /// </summary>
        public void CloseCOMPorts()
        {
            _arduino.ClosePort();
            _pit.ClosePort();
        }

        /// <summary>
        /// Closes other running instances of the system
        /// </summary>
        public void StopOthers()
        {
            _lap.Stop();
        }

        /// <summary>
        /// Gets total distance. 
        /// Used by the LapConfiguration form
        /// </summary>
        /// <returns>Total distance</returns>
        public double GetTotalDistance()
        {
            return Arduino.GetInstance.GetTotalDistance();
        }
    }
}